<?
/**
 * 	The general object class.
 *
 *	@copyright	2010 - oneDot only<br>
 *  You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.<br>
 *  The content is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.  see the GNU General Public License for more details.
 *
 *	@author		Koen Betsens <we@onedotonly.be>
 *  @package	Global
 */

/**
 * The general object class.
 *
 * The object class turns database rows into more friendly Object objects. The object handles the creation, data retreival and data editing - inclusive all referential, media and text connected to it. 
 * Since the Engine structure is object centric, most object handling should happen with this Object class.  
 *
 * <code>
 * local | global $Object = new OBJECT( type, ID )
 * </code>
 *
 * @package		Global
 *
 * @return		Object	general OBJECT
 */
class OBJECT {

	/**
	 * The object ID
	 *
	 * @var	integer
	 */
	public $id;
	
	/**
	 * The object "" type string
	 *
	 * @var	string
	 */
	public $type;

	/**
	 * Constructs the Object object
	 *
	 * @todo	Remove $vars.
	 *
	 * @param 	mixed		$type_or_vars	string type or arry | object with variables
	 * @param 	int			$id				The object ID, incase not yet declared in $type_or_vars. Incase empty, it defaults to the most recent object of that type (or creation of a new object)
	 * @param 	boolean 	$create_new		If true, a new Object of the given type is created. Defaults to false. 
	 * @param 	mixed 		$vars			Deprecated in favour of $type_or_vars, should be deleted.
	 * @return	object
	 */
	function __construct( $type_or_vars, $id = 0, $create_new = false, $vars = false ) {
		global $DB;
		
		if( is_array( $type_or_vars ) || is_object( $type_or_vars )){

				$this->_addvars( $type_or_vars );
		} else 	$this->type = ( is_int( $type_or_vars ))?
				$DB->type_r( $type_or_vars ) : $type_or_vars;
		
#		Load assigned object or most recent
		if( $id || !$create_new )
			$this->load( $this->type, $this->id = ( int )( $id )? $id : $this->id );

#		Create new
		else
			$this->create( $this->type, false, 0, $vars );

#		Set up extended Class
		$this->media = new OBJECT_MEDIA( $this->type, $this->id );
		$this->tags  = new OBJECT_TAGS( $this->type, $this->id );
	}
	
	/**
	 * Extend object with given vars
	 *
	 * Default function.
	 * If $vars is string, a json conversion is attempted. 
	 *
	 * @param 	mixed  $vars	array or object (or json string) with Object variables
	 */
	function _addvars( $vars ){ 
		
		if( is_string( $vars )) $vars = json_decode( $vars );
		
		foreach($vars as $k=>$v) $this->$k = $v;
	}
	
	
	function create( $type, $name = false, $id = 0, $values = false ) {
		global $DB, $Engine;
		
		$input = array( "active"=> 0 );
		
		// UNIFORMIZE THESE...
		if( $DB->column_exists( $type, "creator", 1 ) && !$values[ "creator" ]) $input[ "creator" ] = $Engine->user();
		if( $DB->column_exists( $type, "nameid", 1 )) $input[ "nameid" ] = ( $name )? $Engine->text->set( $name ): 0;
		if( $DB->column_exists( $type, "flog", 1 )) $input[ "flog" ] = date( "Y-m-d H:i:s" );
		if( $DB->column_exists( $type, "llog", 1 )) $input[ "llog" ] = date( "Y-m-d H:i:s" );
		if( $DB->column_exists( $type, "date", 1 )) $input[ "date" ] = date( "Y-m-d H:i:s" );
		
		if( $values )
			foreach( $values as $key => $value )
				if( $DB->column_exists( $type, $key, 1 )) $input[ $key ] = $value;
				else $input[ "meta" ][ $key ] = $value;
		
		$this->id = $DB->set( $type, $input );
		
		return $this->load( $type, $this->id );
	}

	
	public function load( $type, $id ) {
		global $DB;
		
		if( $DB->column_exists( $type, "nameid", 1 )) $nameid = ", <%lang:nameid%> as name";
		
#		Get DB row & decode meta
		$this->values = $DB->get( $type, ( $id )? "ID=" . $DB->smartq( $id, "int" ) : "active order by ID desc", "*$nameid" );
		$this->values [ 'meta' ] = json_decode( $this->values[ 'meta' ]);

		return $this->values;
	}
	
	public function get( $what ) {
		
		
		if( !isset( $this->values[ $what ])) {
			global $Engine;
			
			if ( $what == "content" )
				 $this->values[ $what ] = $Engine->text->get( $this->values[ "textid" ], "long" );
			else $this->values[ $what ] = $this->values[ "meta" ]->{ $what };
		}

		return $this->values[ $what ];
	}
	
	/**
	 *
	 * @todo		should use the updated DB::reference() function (master/slave)	
	 */
	public function reference( $objtype, $multi = 1 ) {
		global $DB;
		
		$results = $DB->reference( $this->type, $this->id, $objtype );
				
		return ( $multi )? $results : $results[0];
	}
	
	
	public function ref_object( $type, $is_master = 0, $table = 'object' ) {
		global $DB;
		
		$results = ( $is_master )?
			$DB->reference( $this->type, (int) $this->id, $type, false, $table ):
			$DB->reference( $type, false, $this->type, $this->id, $table );
				
		if( $results )
			return new OBJECT( is_string( $type )? $type: $results[0]['parenttype'], (int) is_string( $type )? $results[0] : $results[0][ 'parentid' ]);
	}
	
	/**
	 * Returns array with all referential objects
	 *
	 * This should be the preferred object reference function.
	 *
	 * @param 	string		$type		the object type string
	 * @param 	boolean 	$is_master	master | slave position of the object. Defaults to master
	 * @param 	int		 	$limt		Maximal return (to avoid DB drainage). Defaults to 240
	 * @return	string		$table		Reference table, usually "object" (default) or "social". Extendable for custom use.	
	 */
	public function ref_objects( $type, $is_master = true, $limit = 240, $table = "object" ) {
		global $DB;
		
		$results = ( $is_master )?
			$DB->reference( $this->type, (int) $this->id, $type, null, $table ):
			$DB->reference( $type, null, $this->type, $this->id, $table );

		
		if( $results &&
			$objects = $DB->filter_objects( $type, "ID in(" . implode( ",", $results ) . ") && active order by rank desc limit $limit" ))

			return $objects;
	}
	
	public function siblings( $parenttype, $limit = 240 ) {
		global $DB;
		
		if( $Parent = $this->ref_object( $parenttype ))
			return $Parent->ref_objects( $this->type, true, $limit );

	}
	
	public function uri() {
		
		return "/o/{$this->type}/". urlencode( $this->get( "name" )) ."/{$this->id}";
	}
	
	public function url() {
		
		return engine_get( "url" ) . $this->uri();
	}
	
#	Some boolean checks
	public function hasName() {
		
		return ( $this->get( "nameid" ));
	}
	
	public function hasContent() {
		
		return ( $this->get( "textid" ));
	}
	
	public function hasMedia( $objtype = "media.img" ) {
		
		return ( $this->media->reference( $objtype ));
	}
	
	public function hasParent() {
		global $DB;
		
		return ( $DB->query( "select ID from glue_object where objtype={$this->type} && objid={$this->id} limit 1" ));
	}
	
	

	public function set( $key, $value, $is_json = false ) {
		global $DB;
		
		$value = $this->set_cache( $key, $value, $is_json );
		
		$DB->query( "update " . $DB->type( $this->type, "table" ) . " set `$key`='" . (( $is_json )? json_encode( $value ): $value ) . "' where ID=" . $this->id, 0 );
	}


	public function set_cache( $key, $value, $is_json = false ) {
		
		if( $is_json && is_array( $value ) && ( $json = $this->values[ $key ]))
			$value = array_merge(( array ) $json, $value );
		
		$this->values[ $key ] = ( $is_json )? (object) $value : $value;
		
		return $value;
	}


	public function set_reference( $objtype, $objid, $is_master ) {
		global $DB;
		
		$DB->set_reference(	$is_master? $this->type: $objtype,
							$is_master? $this->id: (int) $objid,
							$is_master? $objtype : $this->type,
							$is_master? (int) $objid: $this->id,
							array_pop( explode( "_", $DB->type( $is_master? $this->type: $objtype, "table" ))));
	}

	
	public function active() {

		return (int) $this->values[ 'active' ];
	}
	
	public function dump() {
		global $DB;
		
		$this->set( "active", "0" );
		$this->delete_references();
	}
	
	public function remove() {
		global $DB;
		
		if( $this->id ) {
			$typeid = $DB->type( $this->type );
			$DB->query( "delete from " . $DB->type( $this->type, "table" ) . " where type=$typeid && ID={$this->id} limit 1", 0 );
			$DB->query( "delete from glue_object where ( parenttype=$typeid && parentid={$this->id} ) || ( objtype=$typeid && objid={$this->id} )", 0 );
		}
	}
	
	public function delete_references() {
		global $DB;
		
		if( $this->id ) {
			$typeid = $DB->type( $this->type );
			$DB->query( "delete from glue_object where ( parenttype=$typeid && parentid={$this->id} ) || ( objtype=$typeid && objid={$this->id} )", 0 );
			$DB->query( "delete from glue_social where ( parenttype=$typeid && parentid={$this->id} ) || ( objtype=$typeid && objid={$this->id} )", 0 );
		}
	}
	
	public function json() {
		
		if( $args = func_get_args() )
			foreach( $args as $what ) $json[ $what ] = $this->values[ $what ];
		
		return json_encode(( $args )? $json : $this->values );
		
	}
	
	public function render_core_json () {
		global $DB;
		
		$object = array( "id"=>$this->id, "type"=> $this->type, "list"=> array( $this->id, strlimit( $this->get( "name" ), 72), $this->active() ));
				
		return json_encode( $object );
	}

}




class OBJECT_MEDIA extends MEDIA {
	function __construct( $type, $id ) {
		
		$this->type = $type;
		$this->id = $id;
	}
	
	public function reference( $objtype, $condt = false ) {
		global $DB;

		if( !$condt ) $condt = (int) $this->id;
		
		if( !$this->cache[ $objtype ] )
			$this->cache[ $objtype ] = $DB->reference( $this->type, $condt, $objtype );
			
		if( is_array( 	$this->cache[ $objtype ][0]))
			foreach(	$this->cache[ $objtype ] as $n => $vars )
						$this->cache[ $objtype ][ $n ] = $vars[ "objid" ];
		
		return $this->cache[ $objtype ];
	}
	
	public function filter( $objtype, $condt = "" ) {
		global $DB;
		
		if( $objs = $this->reference( $objtype ))
			$result = $DB->filter( $objtype, "ID in(". implode( ",", $objs ). ") $condt" );
		
		return $result;
	}
	
	public function get( $objtype, $condt = "active order by rank desc, rand()" ) {
		global $DB;

		if( $this->cache[ $objtype ] ||
			$this->cache[ $objtype ] = $DB->reference( $this->type, ( int ) $this->id, $objtype ))
		
				$result = $DB->get( $objtype, "ID in(". implode( ",", $this->cache[ $objtype ]). ") && $condt limit 1" );
		
		$result[ 'meta' ] = ( $result[ 'meta' ])? json_decode( $result[ 'meta' ]) : array();

		return ( object ) $result;
	}
	
	// DEPRECATED:
	/*public function get( $objtype, $order = "desc" ) {
		global $DB;

		if( $this->cache[ $objtype ] ||
			$this->cache[ $objtype ] = $DB->reference( $this->type, "parentid=". $this->id, $objtype ))
		
				$result = $DB->get( $objtype, "ID in(". implode( ",", $this->reference( $objtype )). ")" );

		return $result;
	}*/

	public function get_key( $objtype, $key = "ID" ) {
		global $DB;
		
		if( $this->cache[ $objtype ] ||
			$this->cache[ $objtype ] = $DB->reference( $this->type, "parentid=". $this->id, $objtype ))
		
				$result = $DB->get_key( $objtype, "ID in(". implode( ",", $this->reference( $objtype )). ") order by rank desc, rand()", $key );

		return	$result;
	}
	
	public function render( $objtype = "media.img", $sze = "240.0" ) {
		
		return "<img src='{$this->uri( $objtype, $sze )}' />";
	}
	
	public function uri( $objtype = "media.img", $sze = "240.0" ) {
		
		return "/public.img?id={$this->get_key($objtype)}&sze=$sze";
	}
}

/*

OBJECT::tags::filter_by_group( 24 );

array( group1:[tag1,tag2], group2:[tag3,tag4])
foreach
group1 -

OBJECT::tags( "stijlgroup", 24 );
*/


class OBJECT_TAGS {
	function __construct( $type, $id ) {
		
		$this->type = $type;
		$this->id = $id;
	}
	
	public function referenced( $include_parents = true ) {
		global $DB;

		// return all tags of referenced objects (parents and children);
	}
	
	public function filter() {
		global $DB;
		
		return $DB->tags->filter( $this->type, $this->id );
	}
	
	public function get( $objtype, $condt = "active order by rank desc, rand()" ) {
		global $DB;

		// return random object related tag.
	}
}

?>